package org.hydrakyoufeng.stuplatform.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.hydrakyoufeng.stuplatform.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * 权限拦截器
 */
@Component
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {


    private final RedisTemplate redisTemplate;

    private final JWTUtils jwtUtils;

    /**
     * 用于保存当前登录管理员的身份信息
     */
    private final ThreadLocal<Integer> adminIdTheadLocal;

    private final ThreadLocal<Integer> userIdTheadLocal;

    /**
     * 用户允许访问的URI集合：固定定义在此
     */
    private final Set<String> userURIs = new TreeSet<>(
            Arrays.asList("/class/queryEntryList",
                    "/class/entry",
                    "/class/queryUserList",
                    "/course/queryUserList",
                    "/course/updateUserOperate",
                    "/course/insertCorrecting",
                    "/chapter/queryUserList",
                    "/qiniu/getSourceURL"
            ));

    @Autowired
    public PermissionInterceptor(RedisTemplate redisTemplate,
                                 JWTUtils jwtUtils,
                                 @Qualifier("adminIdTheadLocal") ThreadLocal<Integer> adminIdTheadLocal,
                                 @Qualifier("userIdTheadLocal") ThreadLocal<Integer> userIdTheadLocal) {
        this.redisTemplate = redisTemplate;
        this.jwtUtils = jwtUtils;
        this.adminIdTheadLocal = adminIdTheadLocal;
        this.userIdTheadLocal = userIdTheadLocal;
    }

    /**
     * 调用Controller之前执行：
     * 1.获取请求头信息中的token，判断token是否有效
     * 2.从redis中获取用户权限信息，与当前的uri地址是否匹配
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("拦截器：" + requestURI);
        // 读出用户的身份令牌数据
        String tokenStr = request.getHeader("Authorization");

        response.setContentType("application/json;charset=utf-8");
        if (null == tokenStr) {
            response.getWriter().write("{\"code\":0,\"msg\":\"没有传Token\"}");
            return false;
        }
        // 解密
        Map<String,Object> map = jwtUtils.decodeTokenStr(tokenStr);
        // 生成redis的key
        String key = jwtUtils.generateCacheKey(map,jwtUtils.getIp(request));
        map = (Map<String, Object>) redisTemplate.opsForValue().get(key);
        // 表示已经登录：部分功能登录后就能使用的可以写在这里判断
        if (null != map) {
            if (requestURI.startsWith("/qiniu/")) {
                log.info("登录后可以使用的API：" + requestURI);
                return true;
            }
        }
        // 前端用户
        if ("USR".equals(map.get("type"))) {
            Integer id = (Integer) map.get("id");
            userIdTheadLocal.set(id);
            boolean b = userURIs.stream().anyMatch(s -> requestURI.startsWith(s));
            if (b) {
                return true;
            }
            response.getWriter().write("{\"code\":0,\"msg\":\"没有权限\"}");
            return false;
        }

        Set<String> permissionSet = (Set<String>)map.get("permissionSet");
        Integer id = (Integer) map.get("id");
        adminIdTheadLocal.set(id);
        // 使用流式编程判断uri是否在权限集合中
        boolean b = permissionSet.stream().anyMatch(s -> checkMatches(requestURI,s));
        if (b) {
            return true;
        }
        response.getWriter().write("{\"code\":0,\"msg\":\"没有权限\"}");
        return false;
    }

    /**
     * 调用Controller之后执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 整个请求执行完成之后执行
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清空线程本地对象
        adminIdTheadLocal.remove();
        userIdTheadLocal.remove();
    }

    /**
     * 判断uri与pattern是否匹配
     * ?表示一个字符，*表示0-n个字符串的匹配方法
     * @param uri 匹配字符串
     * @param pattern 匹配规则
     * @return
     */
    private boolean checkMatches(String uri,String pattern) {
        if (null == pattern || null == uri) {
            return uri == pattern;
        }
        if (pattern.indexOf("?") == -1 && pattern.indexOf("*") == -1) {
            return uri.equals(pattern);
        }
        // 先判断没有*号的?号情况
        if (pattern.indexOf("*") == -1) {
            if (uri.length() != pattern.length()) {
                return false;
            }
            char c1;
            char c2;
            for (int i = 0;i < uri.length();i ++) {
                c1 = uri.charAt(i);
                c2 = pattern.charAt(i);
                if (c1 != c2 && c2 != '?') {
                    return false;
                }
            }
            return true;
        }
        // 有*号的
        char c1;
        char c2;
        int pIndex = 0;
        for (int i = 0;i < uri.length();i ++) {
            if (pIndex > pattern.length() - 1) {
                return false;
            }
            c1 = uri.charAt(i);
            c2 = pattern.charAt(pIndex);
            if (c1 != c2) {
                if (c2 == '?') {
                    pIndex ++;
                    continue;
                } else if (c2 == '*') {
                    // 如果已经是最后了，那么成功
                    if (pIndex == pattern.length() - 1) {
                        return true;
                    } else {
                        c2 = pattern.charAt(pIndex + 1);
                        // 如果后面的字符和当前c1的字符一致，那么需要跳过*了
                        if (c1 == c2) {
                            pIndex += 2;
                        }
                    }
                    continue;
                } else {
                    pIndex ++;
                    return false;
                }
            }
            pIndex ++;
        }
        return true;
    }
}
